/** 
 * File: Time.java
 * Project: CMSC 345 Course Registration System, Fall 2010
 * Author: David Chong
 * Date: 12/5/10
 * E-mail: ro62899@umbc.edu
 * This class is used to represent the Time.  The time is in this format hh:mm,
 * "h" represents the hours and "m" stands for minutes.  The number is in 
 * military format (0 to 2359).  Take, for example, the number 2350 would
 * represent 11:59 PM.  There are methods to display the stored number to
 * the 12-hour format.  In addition, exceptions will be thrown if invalid
 * input is passed into the constructor and methods.
 * 
 * Class Invariant: startingTime and endingTime can not be negative
 */

package registeringSystem;

import com.google.appengine.api.datastore.Key;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;


@PersistenceCapable
public class Time 
{
	@PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key theKey;
	
	@Persistent
    private int startingTime;
	
	@Persistent
    private int endingTime;
	
	// (6:00 PM)
	private static int STARTING_EVENING_TIME = 1800;     
	private static final int CONVERSION_TO_12_HOUR_NUMBER = 1200;
    
    public Time()
    {
    	startingTime = 0;
    	endingTime = 0;
    } // end of default constructor
    
    
    /** 
     * Name: Time <br><br>
     * The constructor that initializes it private data member with the data
     * passed in. <br><br>
     * Precondition: None <br>
     * Postcondition: Creates and initializes the object with the values passed
     *                in. <br>
     * @param startingTime - the starting time for the object
     * @param endingTime - the ending time for the object
     * @throws NotValidArgumentException is thrown if any non valid argument is
     *         passed in, a negative value is passed in the integer parameter
     */
    public Time(int startingTime, int endingTime)
                                               throws NotValidArgumentException
    {
    	String errorMessage = checkIfTheInputIsValid(startingTime, endingTime);
    	
    	// there was no error messages
    	if(errorMessage.equals(""))
    	{	
    	    this.startingTime = startingTime;
    	    this.endingTime = endingTime;
    	    //this.isEveningTime = isEveningClass;
    	}
    	
    	else
    		throw new NotValidArgumentException(errorMessage + "\n");
    } // end of constructor
    
    
    
    /** 
     * Name: Time <br><br>
     * The copy constructor that initializes the private data members with the
     * same values with as the argument.  It is a deep copy.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Performs a deep copy of the argument object.
     * @param None
     * @throws NullReferenceException is thrown is a negative number is passed
     *         in.
     */
    public Time(Time copyThis) throws NullReferenceException
    {
    	if(copyThis == null)
    		throw new NullReferenceException("Can not create the " +
    				                         "MonthDayYear because a null" +
    				                         "reference was passed in!\n");
    	
    	this.startingTime = copyThis.startingTime;
    	this.endingTime = copyThis.endingTime;
    } // end of copy constructor
    
    
    
    /** 
     * Name: checkIfTheInputIsValid <br><br>
     * Checks if the input was valid.
     * passed in. <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a String stating if the input was valid. <br>
     * @param startingTime - the starting time for the object
     * @param endingTime - the ending time for the object
     */
    private String checkIfTheInputIsValid(int startingTime, int endingTime)
    {
    	String errorMessage = "";
    	
    	if(startingTime < 0)
    		errorMessage += "A negative was passed into the starting time!\n";
    	
    	else
    		errorMessage +=checkTimeHelper(startingTime);
    	
    	
    	
    	if(endingTime < 0)
    		errorMessage += "A negative was passed into the ending time!\n";
    	
    	else
    		errorMessage += checkTimeHelper(endingTime);
    	
    	
    	if(errorMessage.equals("") == false)
    		errorMessage += "The object was not created!\n\n";
    	
    	return errorMessage;
    } // end of checkIfTheInputIsValid
    
    
    
    /** 
     * Name: checkTimeHelper <br><br>
     * The private helper decides if the time is valid.  The value should
     * be between 0 to 2359.  The last two digits may not exceed 59 (if there
     * at least two digits for the input number).
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a String containing error messages.  The String
     *                may be an empty string. <br>
     * @param theTime - the argument that will be check to see if the number is
     *                  in the correct format.
     */
    private String checkTimeHelper(int theTime)
    {
    	String errorMessage = "";
    	String timeAsAString = Integer.toString(theTime);
    	int length = timeAsAString.length();
    	
    	// more than 4 digits passed in
    	if(length > 4)
    		errorMessage += "The time is not in the correct format!\n"+
    		                "There should only be four digits!\n";
    	
    	else if(length == 4)
    	{
    		String firstTwoDigits = timeAsAString.substring(0, 3);
    		String lastTwoDigits = timeAsAString.substring(2);
    		int convertedTime1 = Integer.parseInt(firstTwoDigits);
    		int convertedTime2 = Integer.parseInt(lastTwoDigits);
    		
    		if(convertedTime1 >= 23)
    			errorMessage += "The first two digits can not be greater " +
    			                "than \"23\"!\n";
    		
    		if(convertedTime2 >= 60)
    			errorMessage += "The last two digits can not be greater " +
    			                 "than \"59\"!\n";
    	} // end of if statement
    	
    	else if(length == 3)
    	{
    		String lastTwoDigits = timeAsAString.substring(1);
            int convertedTime = Integer.parseInt(lastTwoDigits);
    		
    		if(convertedTime >= 60)
    			errorMessage += "The last two digits can not be greater " +
            	                 "\"59\"!\n";
    	} // end of else if statement
    	
    	else if(length == 2)
    	{
            // do not need to convert the time since there is only two digits
    		if(theTime >= 60)
    			errorMessage += "The last two digits can not be greater " +
    			                 "\"59\"!\n";
    	} // end of else if statement
    	
    	return errorMessage;
    } // end of checkTimeHelper()
    
    
    
    /** 
     * Name: getTheKey <br><br>
     * An accessor method that retrieves the the key for the object in the
     * Google datastore.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns the key. <br>
     * @param None
     */
    public Key getTheKey()
    {
    	return theKey;
    } // end of getTheKey()
    
    
    
    /** 
     * Name: getStartingTime <br><br>
     * An accessor method that retrieves the starting time.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns the starting time. <br>
     * @param None
     */
    public int getStartingTime()
    {
    	return startingTime;
    } // end of getStartingTime()
    
    
    
    /** 
     * Name: getEndingTime <br><br>
     * An accessor method that retrieves the ending time.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns the ending time. <br>
     * @param None
     */
    public int getEndingTime()
    {
    	return endingTime;
    } // end of getEndingTime()
    
    
    
    /** 
     * Name: isEveningClass <br><br>
     * The method determines if the starting time is during evening hours.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns the if the time is during the evening hours. <br>
     * @param None
     */
    public boolean isEveningTime()
    {
    	return (startingTime >= STARTING_EVENING_TIME);
    } // end of isEveningClass()
    
    
    
    /** 
     * Name: setStartingTime <br><br>
     * A mutator to change the startingTime field.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Changes the startingTime to the value passed in. <br>
     * @param startingTime - the argument that will be used to set the field
     * @throws NotValidArgument is thrown when a negative value is passed in
     */
    public void setStaringTime(int startingTime) throws NotValidArgumentException
    {
    	if(startingTime < 0)
    		throw new NotValidArgumentException("Did not change the time " + 
                                                "because a negative value " +
                                                "was passed in!\n");
    	
    	this.startingTime = startingTime;
    } // end of setStartingTime()
    
    
    
    /** 
     * Name: setEndingTime <br><br>
     * A mutator to change the endingTime field.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Changes the endingTime to the value passed in. <br>
     * @param endingTime - the argument that will be used to set the field
     * @throws NotValidArgument is thrown when a negative value is passed in
     */
    public void setEndingTime(int endingTime) throws NotValidArgumentException
    {
    	if(endingTime < 0)
    		throw new NotValidArgumentException("Did not change the time " + 
    				                            "because a negative value " +
    				                            "was passed in!\n");
    	
    	this.endingTime = endingTime;
    } // end of setEndingTime()
    
    
    
    /** 
     * Name: showStartingToEndingTime <br><br>
     * Returns a String displaying the starting time and ending time.  The 
     * format of the String will be hh:mm (AM/PM) - hh:mm (AM/PM).  The first
     * part will be the starting time and the ending time is the other part.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a String showing the starting and ending time. <br>
     * @param None
     */
    public String showStartingToEndingTime()
    {
    	return showPrintableForm(startingTime) + " - " +
    	       showPrintableForm(endingTime);
    } // end of showStartingToEndingTime()
    
    
    
    /** 
     * Name: showPrintableForm <br><br>
     * Converts the input time to have a hh:mm format where "h" is between 0 to
     * 23 and "m" is between 0 to 59.  It includes either " PM" or " AM" in the
     * returned String depending on the value passed in.
     * <br><br>
     * Precondition: The argument can not be negative <br>
     * Postcondition: Returns a String in a hh:mm format. <br>
     * @param theTime - the integer taht will be converted to hh:mm format
     */
    private String showPrintableForm(int theTime)
    {
    	String theReturnedTime = "";
    	
    	int theConvertedTime = theTime - CONVERSION_TO_12_HOUR_NUMBER;
    	
    	// it is noon (12:00 PM)
    	if(theConvertedTime == 0)
    		theReturnedTime += "12:00 PM";
    	
    	// time is between 1201 to 2359 (12:01 PM to 23:59 PM)
    	else if(theConvertedTime > 0)
    		theReturnedTime += showPrintableFormHelper(theConvertedTime) 
    		                                                           + " PM";
    	
    	// time is between 0000 to 1159 (12:00 AM to 11:59 AM)
    	else
    		theReturnedTime += showPrintableFormHelper(theTime) + " AM";
    	
    	return theReturnedTime;
    } // end of showPrintableForm()
    
    
    
    /** 
     * Name: showPrintableFormHelper <br><br>
     * Converts the integer argument to have the form hh:mm where "h" is
     * between 0 to 23 and "m" is between 0 to 59.
     * <br><br>
     * Precondition: The time can not be negative <br>
     * Postcondition: Returns a String in the hh:mm format. <br>
     * @param theTime - the time that will be used to create the String
     *                  in a hh:mm format
     */
    private String showPrintableFormHelper(int theTime)
    {
    	String theReturnedTime = "";
    	String tempTimeHolder = Integer.toString(theTime);
		int stringLength = tempTimeHolder.length();
		
		if(stringLength == 1)
			theReturnedTime += "12:0" + tempTimeHolder.charAt(0);
		
		else if(stringLength == 2)
			theReturnedTime += "12:" + tempTimeHolder.substring(0);
		
		else if(stringLength == 3)
			theReturnedTime += tempTimeHolder.charAt(0) + ":" +
			                   tempTimeHolder.substring(1);
		
		else
			theReturnedTime += tempTimeHolder.substring(0, 2) + ":" +
			                   tempTimeHolder.substring(2);
		
		return theReturnedTime;
    } // end of showPrintableFormHelper
    
    
    
    /** 
     * Name: doesTheTimesOverlap <br><br>
     * The constructor that initializes its private data members with the
     * values passed in.  It checks the inputs and it might throw an
     * exception if the preconditions are violated and the object will not
     * be instantiated.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a boolean stating if the Times overlap. <br>
     * @param otherTime - the Time object that will be compared to the
     *                    calling object
     */
    public boolean doesTheTimesOverlap(Time otherTime)
	{
    	boolean doTheyOverlap;
    	
    	if(otherTime == null)
    		doTheyOverlap = false;
    	
    	else
    		doTheyOverlap = (startingTime <= otherTime.getStartingTime() &&
		                     endingTime >= otherTime.getStartingTime()) ||
		                    (otherTime.getStartingTime() <= startingTime &&
		                     otherTime.getEndingTime() >= startingTime);
    	
    	return doTheyOverlap;
	} // end of doesTheTimesOverlap()
    
    
    
    /** 
     * Name: toString <br><br>
     * Displays the object's state.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a String representing the object's state.
     * <br>
     * @param None
     */
    public String toString()
	{
		return "Starting Time:    " + showPrintableForm(startingTime) + "\n" +
		       "Ending Time:      " + showPrintableForm(endingTime) + "\n\n";// +
	} // end of toString()

    
    
	/** 
     * Name: equals <br><br>
     * Compares two objects and determines if they are equal.
     * <br><br>
     * Precondition: None <br>
     * Postcondition: Returns a boolean stating if the object are equal. <br>
     * @param otherTime - the Time that will be compared to the calling object
     */
    public boolean equals(Time otherTime)
    {
    	boolean areTheyEqual;
    	
    	if(otherTime == null)
    		areTheyEqual = false;
    	
    	else
    	    areTheyEqual = startingTime == otherTime.startingTime &&
    	                   endingTime == otherTime.endingTime;// &&
    	                   //isEveningTime == otherTime.isEveningTime;
    	
    	return areTheyEqual;
    } // end of equals()
} // end of Time class